import Axios from 'axios'
import Qs from 'qs'

/**
 * 本Vue插件提供远程访问功能
 *
 */
const Http = {
  /**
   *
   * @param Vue
   * @param options
   */
  install: function (Vue, options = {}) {
    if (Http.installed) return
    Http.installed = true

    const service = Axios.create(options)

    /**
     * @typedef {{url?: string, layer?: string}} Redirect
     */

    /**
     * @typedef Action
     * @type {Object}
     * @property {string} [url=]
     * @property {boolean} [post=false]
     * @property {Redirect} [redirect=]
     */

    /**
     * @callback Receiver 数据处理器
     * @param {Object} data Ajax获取到的数据
     * @returns Object
     */

    /**
     * @callback Handler 处理器
     * @param {Action} action
     * @param {Object} data
     * @returns boolean
     */

    /**
     * @type {Receiver[]} 数据处理器数组
     */
    const receivers = []

    /**
     * @type {Handler[]}
     */
    const beforeSendHandlers = []

    /**
     * 异常处理
     * @type {Receiver}
     */
    let exceptionHandler = null

    /**
     * 重定向处理
     * @type {Receiver}
     */
    // let redirectHandler = null

    Http.onException = (vue, handler) => {
      exceptionHandler = handler
      vue.$on('hook:beforeDestroy', () => {
        if (handler === exceptionHandler) exceptionHandler = null
      })
    }

    Http.onReceive = (vue, receiver) => {
      const id = receivers.push(receiver)
      vue.beforeDestroy = () => receivers.splice(id, 1)
    }

    Http.beforeSend = (vue, handler) => {
      const id = beforeSendHandlers.push(handler)
      vue.beforeDestroy = () => beforeSendHandlers.splice(id, 1)
    }

    // Http.onRedirect = (vue, receiver) => {
    //   redirectHandler = receiver
    //   vue.$on('hook:beforeDestroy', () => {
    //     if (receiver === redirectHandler) redirectHandler = null
    //   })
    // }

    /**
     * @callback Then 回调
     * @param {Object} data
     * @returns
     */

    /**
     * 发送请求
     * @param {Action} action 请求对象
     * @param {?{}} value 参数对象
     * @returns {Promise<Object>}
     */
    Http.Send = async (action, value = null) => {
      // eslint-disable-next-line prefer-promise-reject-errors
      if (beforeSendHandlers.length > 0) for (let fn of beforeSendHandlers) if (await fn(action, value) === false) return Promise.reject()

      const {
        url = null,
        post = false
      } = action
      // 提取Url
      if (!url) return Promise.reject(Error('找不到的URL参数'))

      // 组建option
      const option = { url }

      if (post) {
        option.method = 'post'
        option.headers = { 'Content-Type': 'application/x-www-form-urlencoded' }
        if (value) option.data = Qs.stringify(value)
      } else {
        option.method = 'get'
        if (value) option.params = JSON.parse(JSON.stringify(value))
      }

      // 开启跨域cookie
      option.withCredentials = true
      option.paramsSerializer = params => Qs.stringify(params, { arrayFormat: 'brackets' })

      try {
        const response = await service(option)
        if (!response.data) return null;

        if (receivers.length > 0) for (let fn of receivers) if (await fn(response.data) === false) return null

        return response.data

      } catch (e) {
        exceptionHandler && exceptionHandler(e)
        return Promise.reject(e)
      }
    }

    /**
     * Get
     * @param {String} url
     * @param {?Object} value
     * @returns {Promise<Object|*>}
     */
    Http.Get = async (url, value = null) => Http.Send({ url }, value)

    Http.Post = async (url, value = null) => Http.Send({ url, post: true }, value)

    Http.Fire = async (action, row) => {
      let value

      // 处理数据
      if (action.payload === true) { // 过滤ID
        value = { id: row.id }
      } else if (action.payload === undefined) { // 直接发送
        value = row
      } else if (typeof action.payload === 'string') { // 修改指定元素值
        value = {
          name: action.payload,
          id: row.id,
          value: action.value
        }
      }
      return /* await */ Http.Send(action, value)
    }

    Object.defineProperty(Vue.prototype, '$http', { get () { return Http } })

    Vue.$http = Http
  }
}

export default Http
